package com.tengju.bff.interfaces.manage.crm.report.facade;

import com.beust.jcommander.internal.Maps;
import com.tengju.bff.interfaces.shared.*;
import com.tengju.bff.interfaces.shared.Formatter;
import com.tengju.bff.interfaces.shared.render.ChartVO;
import com.tengju.bff.interfaces.shared.render.KVAssembler;
import com.tengju.bff.interfaces.shared.render.PieAssembler;
import com.tengju.bff.interfaces.shared.render.PieVO;
import com.tengju.data.domain.model.analysis.Meta;
import com.tengju.data.domain.model.analysis.MetaHolder;
import com.tengju.data.domain.model.analysis.Row;
import com.tengju.data.domain.model.report.secmonth.SecMonthReportRepository;
import com.tengju.data.domain.model.user.UserLevelEnum;
import com.tengju.data.domain.shared.CastUtil;
import com.tengju.motivation.application.shared.ApplicationException;
import com.tengju.user.domain.model.user.StarLevelEnum;
import com.tengju.user.domain.model.user.UserIdCode;
import com.tengju.user.domain.model.user.UserInfo;
import com.tengju.user.domain.model.user.UserInfoRepository;
import com.tengju.user.domain.service.RemoteUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author chenhang
 * @Date 2021/7/12
 */
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class SecMonthReportFacade {

    private final MetaHolder metaHolder;
    private final SecMonthReportRepository secMonthReportRepository;
    private final RemoteUserService remoteUserService;
    private final UserInfoRepository userInfoRepository;


    private List<String> PROFIT_RANGE_SET = List.of("0K","0-1K","1-3K","3-6K","6-8K","8-10K","10-50K","50K以上");
    private Map<String,Integer> PROFIT_RANGE_MAP = Map.of("0K",0,
            "0-1K", 1,
            "1-3K",2,
            "3-6K",3,
            "6-8K",4,
            "8-10K",5,
            "10-50K",6,
            "50K以上",7);



    public List<KVVO> profitAnalysis(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_profit_analysis").values();
        List<Row> rows = secMonthReportRepository.profitAnalysis(idCode, month);
        Row row = new Row();
        if (!CollectionUtils.isEmpty(rows)) {
            row = rows.get(0);
        }
        return KVAssembler.buildData(metas, row, List.of("last_year_month_profit_ratio","last_month_profit_ratio"));
    }

    public PieVO profitPie(Long idCode, Integer month) {
        List<Row> rows = secMonthReportRepository.profitAnalysis(idCode, month);
        if (CollectionUtils.isEmpty(rows)) {
            return null;
        }
        Row row = rows.get(0);
        PieVO pie = new PieVO("本月收益分布");
        pie.add("代理服务费", row.getValue("abm_proxy_profit"))
                .add("推广服务费", row.getValue("abm_promote_profit"))
                .add("渠道服务费", row.getValue("tj_profit"));
        return pie;
    }

    public TableVO profitDistributedAnalysis(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_profit_distributed_analysis").values();
        TableVO table = new TableVO();
        TableAssembler.buildColumns(table, metas);
        List<Row> rows = secMonthReportRepository.profitDistributedAnalysis(idCode, month);
        if (CollectionUtils.isEmpty(rows)) {
            rows = new ArrayList<>();
        }
        rows = rows.stream().map(row -> {
            String profitRange = CastUtil.castString(row.getValue("profit_range"));
            row.put("order_type",PROFIT_RANGE_MAP.get(profitRange));
            return row;
        }).sorted(Comparator.comparingInt(row -> CastUtil.castInt(row.getValue("order_type"))))
        .collect(Collectors.toList());
        for (int i = 0; i < PROFIT_RANGE_SET.size(); i++) {
            String profitRangeValue = PROFIT_RANGE_SET.get(i);
            if (rows.size() > i && profitRangeValue.equals(CastUtil.castString(rows.get(i).getValue("profit_range")))) {
                continue;
            }
            rows.add(i,new Row(Map.of("profit_range",profitRangeValue)));
        }
        TableAssembler.buildData(table, rows, metas);
        return table;
    }

    public ChartVO profitDistributedChart(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_profit_distributed_chart_analysis").values();
        List<Row> rows = secMonthReportRepository.profitDistributedAnalysis(idCode, month);
        ChartVO chart = new ChartVO("收益分布");
        chart.addLegend(List.of("本月占比", "上月占比"))
                .buildXAxis(rows, "profit_range")
                .buildSeries(rows, metas);
        return chart;
    }

    public TableVO achievementAnalysis(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_achievement_analysis").values();
        List<Row> rows = secMonthReportRepository.achievementAnalysis(idCode, month);
        TableVO table = new TableVO();
        TableAssembler.buildColumns(table, metas);
        if (CollectionUtils.isEmpty(rows)) {
            return table;
        }
        TableAssembler.buildData(table, rows, metas, List.of("rank", "last_month_rank", "last_month_amt_ratio", "last_year_month_amt_ratio", "last_month_rank"));
        table.resolveRow(row -> {
            int rank = CastUtil.castInt(row.get("rank"));
            int lastMonthRank = CastUtil.castInt(row.get("last_month_rank"));
            row.put("rankVO", RankVO.of(rank, lastMonthRank));
            AmountVO amt = (AmountVO)row.get("amt");
            amt.addExtra(RankVO.of(rank, lastMonthRank));

            AmountVO lastYearMonthAmt = (AmountVO)row.get("lastYearMonthAmt");
            lastYearMonthAmt.addExtra(KVVO.of(
                    "去年同比",
                    Formatter.percent(CastUtil.castDouble(row.get("last_year_month_amt_ratio")),2)
            ));
            AmountVO lastMonthAmt = (AmountVO)row.get("lastMonthAmt");
            lastMonthAmt.addExtra(KVVO.of(
                    "上月环比",
                    Formatter.percent(CastUtil.castDouble(row.get("last_month_amt_ratio")),2)
            ));
        });
        return table;
    }

    public TableVO brandGmvAnalysis(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_brand_gmv_analysis").values();

        TableVO table = new TableVO();
        TableAssembler.buildColumns(table, metas);

        List<Row> rows = secMonthReportRepository.brandGmvAnalysis(idCode, month);
        if (CollectionUtils.isEmpty(rows)) {
            return table;
        }
        TableAssembler.buildData(table, rows, metas);
        return table;
    }

    public TableVO teamRegionAnalysis(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_team_region_analysis").values();

        TableVO table = new TableVO();
        TableAssembler.buildColumns(table, metas);

        List<Row> rows = secMonthReportRepository.teamRegionAnalysis(idCode, month);
        if (CollectionUtils.isEmpty(rows)) {
            return table;
        }
        TableAssembler.buildData(table, rows, metas);
        return table;
    }

    public TableVO teamAnalysis(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_team_analysis").values();

        TableVO table = new TableVO();
        table.addColumn(ColumnVO.of("showName", "类型", false))
                .addColumn(ColumnVO.of("5", "经销商", false))
                .addColumn(ColumnVO.of("4", "黑钻", false))
                .addColumn(ColumnVO.of("3", "白金", false))
                .addColumn(ColumnVO.of("2", "粉卡", false));

        List<Row> rows = secMonthReportRepository.teamAnalysis(idCode, month);
        if (CollectionUtils.isEmpty(rows)) {
            return table;
        }
        Map<String, Row> levelRowMap = rows.stream()
                .collect(Collectors.toMap(
                        k -> CastUtil.castString(k.getValue("level")),
                        v -> v,
                        (v1, v2) -> v1)
                );
        TableAssembler.buildVerticalData(table, levelRowMap, metas);
        return table;
    }

    public PieVO teamAnalysisPie(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_team_pie_analysis").values();
        Meta keyMeta = metas.stream()
                .findFirst()
                .orElseGet(() -> {
                    throw ApplicationException.error("团队总数分布缺少meta！");
                });
        List<Row> rows = secMonthReportRepository.teamAnalysis(idCode, month);
        PieVO pie = new PieVO("团队总数分布");
        PieAssembler.buildData(
                pie,
                keyMeta,
                rows,
                row -> {
                    int level = CastUtil.castInt(row.getValue("level"));
                    UserLevelEnum userLevelEnum = UserLevelEnum.getByCode(level);
                    row.put(PieAssembler.PIE_NAME_KEY, userLevelEnum.getLevelDesc());
                    return row;
                });
        return pie;
    }

    public PieVO teamNewUserPie(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_team_pull_pie_analysis").values();
        Meta keyMeta = metas.stream()
                .findFirst()
                .orElseGet(() -> {
                    throw ApplicationException.error("团队总数分布缺少meta！");
                });
        List<Row> rows = secMonthReportRepository.teamAnalysis(idCode, month);
        PieVO pie = new PieVO("本月新增分布");
        PieAssembler.buildData(
                pie,
                keyMeta,
                rows,
                row -> {
                    int level = CastUtil.castInt(row.getValue("level"));
                    UserLevelEnum userLevelEnum = UserLevelEnum.getByCode(level);
                    row.put(PieAssembler.PIE_NAME_KEY, userLevelEnum.getLevelDesc());
                    return row;
                });
        return pie;
    }

    public TableVO teamUserTopTable(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_team_user_top").values();
        TableVO table = new TableVO();
        TableAssembler.buildColumns(table, metas);
        table.addColumn(null, ColumnVO.of("showName", "用户ID", false));
        List<Row> rows = secMonthReportRepository.listTeamUserTop(idCode, month);
        if (CollectionUtils.isEmpty(rows)) {
            return table;
        }
        TableAssembler.buildData(table, rows, metas, List.of("id_code","last_month_achievement_ratio"));

        Map<Long, UserInfo> userInfoMap = getUserInfoMap(rows);
        Map<Long, UserInfo> remoteUserInfoMap = getRemoteUserInfoMap(rows);

        table.resolveRow(record -> {

            AmountVO lastMonthAchievement = (AmountVO)record.get("lastMonthAchievement");
            lastMonthAchievement.addExtra(KVVO.of(
                    "上月环比",
                    Formatter.percent(CastUtil.castDouble(record.get("last_month_achievement_ratio")),2)
            ));


            long idCodeVal = CastUtil.castLong(record.get("id_code"));
            StarLevelEnum starLevelEnum = StarLevelEnum.NONE;
            UserInfo userInfo = userInfoMap.get(idCodeVal);
            String nickName = "";
            if (userInfo != null) {
                Integer starLevel = userInfo.getStarLevel();
                starLevelEnum = StarLevelEnum.parseByCode(starLevel);
                nickName = CastUtil.castString(userInfo.getNickName());
            }
            if (StringUtils.isBlank(nickName) && remoteUserInfoMap.containsKey(idCodeVal)) {
                nickName = CastUtil.castString(remoteUserInfoMap.get(idCodeVal).getNickName());
            }
            String showName = nickName + " " +
                    starLevelEnum.getShortName() + "\r\n" +
                    "(" + idCodeVal + ")";
            record.put("showName", showName);

        });
        return table;
    }

    public TableVO teamUserTable(Long idCode, Integer month) {
        Collection<Meta> metas = metaHolder.listMeta("month_report_team_user").values();
        TableVO table = new TableVO();
        TableAssembler.buildColumns(table, metas);
        table.addColumn(null,ColumnVO.of("id_code","直属服务商",false))
                .addColumn("nick_name",ColumnVO.of("star_level_desc","服务商等级",false));

        List<Row> rows = secMonthReportRepository.listTeamUser(idCode, month);
        if (CollectionUtils.isEmpty(rows)) {
            return table;
        }

        TableAssembler.buildData(table, rows, metas, List.of("id_code","star_level"));

        Map<Long, UserInfo> userInfoMap = getUserInfoMap(rows);
        table.resolveRow(record -> {
            long idCodeVal = CastUtil.castLong(record.get("id_code"));
            UserInfo userInfo = userInfoMap.get(idCodeVal);
            String nickName = CastUtil.castString(record.get("nick_name"));
            if (StringUtils.isBlank(nickName) && userInfo != null) {
                record.put("nick_name",userInfo.getBackNickName());
            }
            int level = CastUtil.castInt(record.get("star_level"));
            StarLevelEnum starLevelEnum = StarLevelEnum.parseByCode(level);
            record.put("star_level_desc",starLevelEnum.getDesc());
        });
        return table;
    }

    private Map<Long, UserInfo> getUserInfoMap(List<Row> rows) {
        List<UserIdCode> idCodeList = rows.stream()
                .map(row -> CastUtil.castLong(row.getValue("id_code")))
                .map(UserIdCode::new)
                .collect(Collectors.toList());
        List<UserInfo> userInfoList = userInfoRepository.batchGetUserInfo(idCodeList);
        Map<Long, UserInfo> userInfoMap = Maps.newHashMap();
        if (!CollectionUtils.isEmpty(userInfoList)) {
            userInfoMap.putAll(
                    userInfoList.stream()
                            .collect(Collectors.toMap(k -> k.getIdCode().getValue(), v -> v, (v1, v2) -> v1))
            );
        }
        return userInfoMap;
    }

    private Map<Long, UserInfo> getRemoteUserInfoMap(List<Row> rows) {
        List<UserIdCode> idCodeList = rows.stream()
                .map(row -> CastUtil.castLong(row.getValue("id_code")))
                .map(UserIdCode::new)
                .collect(Collectors.toList());
        List<UserInfo> remoteUserInfos = remoteUserService.getOnlyUserByIdList(idCodeList);
        if (CollectionUtils.isEmpty(remoteUserInfos)) {
            return Collections.emptyMap();
        }
        return remoteUserInfos.stream()
                .collect(Collectors.toMap(k -> k.getIdCode().getValue(),v -> v,(v1,v2) -> v1));
    }
}
